package com.squid.cloud.common.component.generaterno;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.squid.cloud.common.enums.GeneraterNoEnum;
import com.squid.cloud.common.exception.OutOfMaxIndexException;
import com.squid.cloud.model.dao.CodeEntityMapper;
import com.squid.cloud.model.entity.CodeEntity;
import com.squid.cloud.model.entity.CodeEntityExample;
import com.squid.cloud.model.entity.CodeEntityExample.Criteria;

import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
public class GeneraterNo  implements InitializingBean{

	public static final String IP_MAPPING_SERVER_CODE = "IP_MAPPING_SERVER_CODE";
	private static final int MAX_INDEX = 9999;
	private static final String INDEX_FORMAT = "%0" + (MAX_INDEX + "").length() + "d";
	private static final String TIME_FORMAT = "%1$ty%1$tm%1$td%1$tH%1$tM%1$tS";
	private String serverCode;
	private Map<GeneraterNoEnum, AddSelfer> addSelfers;
	/**
	 * 引用名称
	 */
	@Value("${server.name}")
	private String serverName;
	
	@Autowired
	CodeEntityMapper codeEntityMapper;

	private class AddSelfer {
		private int i = 0;
		private String time = "";
		public int n(String time) throws OutOfMaxIndexException {
			if (!this.time.equals(time)) {
				i = 1;
				this.time = time;
			}
			if (i == MAX_INDEX)
				throw new OutOfMaxIndexException();
			return i++;
		}
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		this.serverCode = String.format("%03d", getServerCode());
		addSelfers = new HashMap<GeneraterNoEnum, AddSelfer>();
		for (GeneraterNoEnum type : GeneraterNoEnum.values()) {
			addSelfers.put(type, new AddSelfer());
		}
	}

	private int getServerCode() throws Exception {
		int code = 0;
		// 查询m_code表中的ip配置编号
		CodeEntityExample example = new CodeEntityExample();
		Criteria cra = example.createCriteria();
		cra.andCodeNameEqualTo(IP_MAPPING_SERVER_CODE);
		List<CodeEntity> codeEntityList = codeEntityMapper.selectByExample(example);
		if (codeEntityList == null || codeEntityList.size() <= 0) {
			throw new IllegalArgumentException("m_code表中未配置ip对应的code,配置规则: CODE_NAME:IP_MAPPING_SERVER_CODE, CODE_KEY:IP地址, CODE_VALUE:IP对应数值编码");
		}
		Map<String, Object> ipMappingCodeServiceMap = new HashMap<String, Object>();
		codeEntityList.forEach(codeEntity -> {
			ipMappingCodeServiceMap.put(codeEntity.getCodeKey(), codeEntity.getCodeValue());
		});
		if (ipMappingCodeServiceMap == null || ipMappingCodeServiceMap.size() <= 0) {
			throw new IllegalArgumentException("m_code表中未配置ip对应的code,配置规则: CODE_NAME:IP_MAPPING_SERVER_CODE, CODE_KEY:IP地址, CODE_VALUE:IP对应数值编码");
		}
		if (ipMappingCodeServiceMap.containsKey("*")) {
			Object codeObj = ipMappingCodeServiceMap.get("*");
			try {
				code = Integer.valueOf(codeObj.toString());
			} catch (NumberFormatException e) {
				throw new IllegalArgumentException("m_code表中ip配置对应的code需为数值类型,配置规则: CODE_NAME:IP_MAPPING_SERVER_CODE, CODE_KEY:IP地址, CODE_VALUE:IP对应数值编码");
			}
			return code;
		}
		Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
		InetAddress ip = null;
		while (netInterfaces.hasMoreElements()) {
			NetworkInterface ni = (NetworkInterface) netInterfaces.nextElement();
			Enumeration<InetAddress> ips = ni.getInetAddresses();
			while (ips.hasMoreElements()) {
				ip = ips.nextElement();
				String address = ip.getHostAddress();
				Integer serverCode = null;
				log.info("系统读取到ip地址为:" + address);
				if (ipMappingCodeServiceMap.get(address+"_"+serverName) == null ) {
					break;
				}
				Object codeObj = ipMappingCodeServiceMap.get(address+"_"+serverName);
				try {
					serverCode = Integer.valueOf(codeObj.toString());
				} catch (NumberFormatException e) {
					throw new IllegalArgumentException(
							"m_code表中ip:" + address + " 配置对应的code需为数值类型,配置规则: CODE_NAME:IP_MAPPING_SERVER_CODE, CODE_KEY:IP地址, CODE_VALUE:IP对应数值编码");
				}
				if (serverCode != null) {
					code = serverCode;
					log.info("匹配到服务器编号：" + code);
					return code;
				}
			}
		}
		throw new IllegalArgumentException("m_code表中未找到ip配置对应的code,配置规则: CODE_NAME:IP_MAPPING_SERVER_CODE, CODE_KEY:IP地址, CODE_VALUE:IP对应数值编码");
	}

	/**
	 * 获取一个指定编号类型的唯一编号 
	 * 编号格式为 1位编号类型 + 12位时间戳 + 3位服务器code + 4位自增码 
	 * e.g. A1607071050170017487    
	 * A 代表订单类型
	 * 160707105017  代表 2016年7月7日 早上10点50分17秒
	 * 001 代表服务器编号
	 * 7487 自增值
	 * 一秒之内只能支持9999个编号，如果超过9999编号，则会延迟0.1秒后重新生成。
	 * 既此方法的瓶颈值是每分钟只能处理60万条订单。 可以通过修改MAX_INDEX 值提升瓶颈值，但后果是增加订单编号实际长度
	 * 
	 * @param type
	 * @return
	 * @throws
	 */
	public String newNo(GeneraterNoEnum type) {
		AddSelfer as = addSelfers.get(type);
		String time;
		int index;
		try {
			synchronized (type) {
				time = String.format(TIME_FORMAT, System.currentTimeMillis());
				index = as.n(time);
			}
			String i = String.format(INDEX_FORMAT, index);
			return new StringBuilder().append(type.code).append(time).append(serverCode).append(i).toString();
		} catch (OutOfMaxIndexException e) { 
			try {
				Thread.sleep(100);
				return newNo(type);
			} catch (InterruptedException e1) {
				return newNo(type);
			}
		}
	}

	/**
	 * 生成一个唯一的订单号
	 * 
	 * @return
	 * @throws  
	 */
	public String newOrderNo() {
		return newNo(GeneraterNoEnum.Order);
	}

}
